
//globals
var camera, scene, renderer;
var clock, controls;
var stats;
var blocks = [];
var xLight, yLight, zLight;
var lights = {};
var start_animation = false;
var blueman = {};

var vX = new THREE.Vector3(1,0,0), 
vY = new THREE.Vector3(0,1,0), 
vZ = new THREE.Vector3(0,0,1);

var CA_cube = {};

var State = function(s) {
    this.on = s;
};

var OFF = new State(false);
var ON = new State(true);

var Cell = function(rule) {
    this.on = false;
    this.state = [];
    this.on ? this.state[0] = ON : this.state[0] = OFF;
    //this.state[0] = new State(false);
    this.speed = 0.0;
    this.time;
    this.rule = rule;
    this.nCells = [];
    this.NULL = false;
};

Cell.prototype.clear = function() {
    var c = this;
    c.on = false;
    c.state.length = 0;
    c.state[0] = OFF;
    c.time = 0;
}

Cell.prototype.set_ncells = function(ncells) {
    var c = this;
    c.nCells = ncells;
}

Cell.prototype.set_state_at = function(t, s) {
    var c = this;
    c.state[t] = s;
}

Cell.prototype.getStateAt = function(t) {
    //console.log("getting cell state at: ", t);
    var c = this;
    if(c.state[t] === undefined) {
        c.state[t] = c.rule(c.getStateAt(t-1), c.nCellsStatesAt(t-1));
        return c.state[t];
    }
    else {
        return c.state[t];
    }
}

Cell.prototype.nCellsStatesAt = function(t) {
    var nstates = [];
    var c = this;
    
    for(var i=0; i<c.nCells.length; i++) {
        if(c.nCells[i].NULL === false) {
            nstates[i] = c.nCells[i].getStateAt(t);
        }
    }

    return nstates;
}

//null cells are not simulated and ignored as neighbors
var NC = new Cell(false);
NC.NULL = true;

var CA = {
    initial: OFF
};

CA.game_of_life = function(state, nstates) {
    var liven = 0;
    for(var i=0; i<nstates.length; i++) {
        if(nstates[i].on === true) {
            liven++;
        }
    }

    if(state.on === true && liven < 2) {
        return OFF;
    }

    if(state.on === true && (liven === 2 || liven === 3)) {
        return ON;
    }

    if(state.on === true && liven > 3) {
        return OFF;
    }

    if(state.on === false && liven === 3) {
        return ON;
    }

    return OFF;
}

//special infinite pattern
CA.game_of_life.infinite_pattern = function(grid) {
    if(grid.size < 10) {
        console.log("CA.game_of_life: this pattern requires at least a 10x10 grid..");
    }

    //special pattern...
    grid.set_cell(1,1,0,true).mesh.material.visible = true;
    grid.set_cell(3,1,0,true).mesh.material.visible = true;
    grid.set_cell(3,2,0,true).mesh.material.visible = true;
    grid.set_cell(5,3,0,true).mesh.material.visible = true;
    grid.set_cell(5,4,0,true).mesh.material.visible = true;
    grid.set_cell(5,5,0,true).mesh.material.visible = true;
    grid.set_cell(7,4,0,true).mesh.material.visible = true;
    grid.set_cell(7,5,0,true).mesh.material.visible = true;
    grid.set_cell(7,6,0,true).mesh.material.visible = true;
    grid.set_cell(8,5,0,true).mesh.material.visible = true;
}

CA.rule_110 = function(state, nstates) {
    for(var i=0; i<nstates.length; i++) {
        if(nstates[i].on === true) {
            return ON;
        }
    }
    return OFF;
}

function fill_scene() {
    //build_block(0,0,0).disable_anim = true;
    //blueman = build_blueman(6,2,0);

    CA_cube = build_ca_cube(0,0,0,10,0.5);
    var s = "Cube.";
    scene.add(doll.text.print(s,0,8,1));

//    scene.add(new doll.ui.selectable(new THREE.Vector3(-2,4,0)));
    
    //ground
    var size = 100;
    var ground = new THREE.Mesh(
        new THREE.PlaneGeometry(size, size, size*0.1 * size*0.1),
        new THREE.MeshBasicMaterial( {color: 0x000000} ));
    ground.rotation.x = -Math.PI/2;//so that the normal is y
    scene.add(ground);
        

    //lighting
    xLight = new THREE.SpotLight(0xffffff, 0.5);
    xLight.position.set(100,0,0);
    scene.add(xLight);
    yLight = new THREE.SpotLight(0xffffff, 0.5);
    yLight.position.set(0,100,0);
    scene.add(yLight);
    zLight = new THREE.SpotLight(0xffffff, 0.5);
    zLight.position.set(0,0,100);
    scene.add(zLight);
    lights.object3d = new THREE.Object3D();
    lights.object3d.add(xLight);
    lights.object3d.add(yLight);
    lights.object3d.add(zLight);
    scene.add(lights.object3d);
}


function build_blueman(x,y,z) {
    var b = {};
    b.object3d = new THREE.Object3D();
    b.head_size = 3;
    b.head = new THREE.Mesh(
        new THREE.CubeGeometry(b.head_size, b.head_size, b.head_size),
        new THREE.MeshLambertMaterial({color: 0x0000FF})
    );

    b.object3d.add(b.head);
    b.object3d.position.set(x,y,z);

    scene.add(b.object3d);
    return b;
}

function animate_person(p,delta) {

}

//colors
var red = new THREE.Color("red");
var green = new THREE.Color("green");
var blue = new THREE.Color("blue");


lights.start=false;
lights.timer = 0.0;
lights.spin_speed = 1.0;
lights.spin_dir = 1.0;
lights.pulse_timer = 0.0;

lights.animate = function(delta) {
    if(lights.start === false && lights.timer < 60) {
        lights.timer += delta;
        return;
    }
    else if(lights.start === false) {
        lights.start = true;
        blocks[0].same_color = false;
        blocks[0].color_timer = 100;
        blocks[0].object3d.position.z += 7;
        build_block(blocks[0].object3d.position.x+10,blocks[0].object3d.position.y+10,blocks[0].object3d.position.z-5);
        build_block(blocks[0].object3d.position.x+10,blocks[0].object3d.position.y-10,blocks[0].object3d.position.z-5);
        build_block(blocks[0].object3d.position.x-10,blocks[0].object3d.position.y+10,blocks[0].object3d.position.z-5);
        build_block(blocks[0].object3d.position.x-10,blocks[0].object3d.position.y-10,blocks[0].object3d.position.z-5);
        camera.position.z += 14;
    }
    
    lights.timer += delta;
    lights.pulse_timer += delta;

    lights.object3d.rotation.x += lights.spin_dir * lights.spin_speed * delta;
    lights.object3d.rotation.y += lights.spin_dir * lights.spin_speed * delta;
    lights.object3d.rotation.z += lights.spin_dir * lights.spin_speed * delta;

    if(lights.timer > 1.0) {
        for(i in lights.object3d.children) {
            lights.object3d.children[i].intensity += THREE.Math.randFloat(0.0,0.3);
        }
        lights.timer = 0.0;
    }

    if(lights.pulse_timer > 2.0) {
        if(lights.object3d.children[i].intensity > 0.5) {
            lights.object3d.children[i].intensity = 0.5;
        }
        else {
            lights.object3d.children[i].intensity = 1.5;
        }
        lights.pulse_timer = 0.0;
    }

}

function animate_block(block, delta) {
    if(block.disable_anim === true) {
        return;
    }

    block.color_timer += delta;

    block.color_hue_timer += delta;


    block.spin_timer += delta; 

    if(block.spin === true) {
        if(block.spin_timer > 10.0) {
            //console.log('changing spin...');
            block.spin_dir = -block.spin_dir;
            block.spin_timer = 0.0;
        }
        block.object3d.rotation.x += block.spin_dir * block.spin_speed * delta;
        block.object3d.rotation.y += block.spin_dir * block.spin_speed * delta;
    }

    if(block.color_timer > 5.0) {
        //console.log('changing color...');
        var r =  THREE.Math.randFloat(0,1);

        if(block.color.r > 0) { //its red
            r > 0.5 ? block.color.copy(green) : block.color.copy(blue);
        }
        else if(block.color.g > 0) { //its green
            r > 0.5 ? block.color.copy(blue) : block.color.copy(red);
        }
        else if(block.color.b > 0) { //its blue
            r > 0.5 ? block.color.copy(green) : block.color.copy(red);
        }
        block.color_timer = 0.0;

        var m = 7;
        var n = 0;
        var color;
        block.same_color ? color = new THREE.Color().copy(block.color) 
            : color =  new THREE.Color().setRGB(
                THREE.Math.randFloat(0,1),
                THREE.Math.ranloat(0,1),
                THREE.Math.randFloat(0,1));
        
        for( i in block.object3d.children ) {
            n++;
            if(block.same_color === true) {
                color = block.color;
            }
            else if(n > m) {
                color = new THREE.Color().setRGB(
                    THREE.Math.randFloat(0,1),
                    THREE.Math.randFloat(0,1),
                    THREE.Math.randFloat(0,1));
                n = 0;
            }

            var c = block.object3d.children[i];

            if(block.same_color === true) {
                if(block.color === red) {
                    c.material.color.copy(color);
                }
                else if(block.color === green) {
                    c.material.color.copy(color);
                }
                else {
                    c.material.color.copy(color);
                }
            }
            else {
                c.material.color.copy(color);
            }
        }
    }

    if(block.color_hue_timer > 1.0) {
        //console.log('changing hue...');
        for( i in block.object3d.children ) {
            var c = block.object3d.children[i];
            var r = THREE.Math.randFloat(-0.3, 0.3);
            //c.material.color.copy(block.color);

            if(block.color.r > 0) {
                c.material.color.r -= r;
            }
            else if(block.color.g > 0) {
                c.material.color.g -= r;
            }
            else {
                c.material.color.b -= r;
            }
        }
        block.color_hue_timer = 0.0;
    }
}

function build_block(X,Y,Z) {
    var block = {};
    block.same_color = true;
    block.size = 2;
    block.spin = true;
    block.spin_dir = 1.0;
    block.spin_speed = 1.0;
    block.color = new THREE.Color().copy(blue);
    block.color_timer = 0.0;
    block.color_hue_timer = 0.0;
    block.spin_timer = 0.0;
    block.disable_anim = false;
    block.object3d = new THREE.Object3D();

    for(var x=-block.size; x<block.size; x++) {
        for(var y=-block.size; y<block.size; y++) {
            for(var z=-block.size; z<block.size; z++) {
                block_add_cube(block,x,y,z);
            }
        }
    }

    scene.add(block.object3d);
    block.object3d.position.set(X,Y,Z);

    blocks[blocks.length] = block;
    return block;
}

function block_add_cube(block, x, y, z) {
    var geometry = new THREE.CubeGeometry(1,1,1);
    var material = new THREE.MeshLambertMaterial( { } );
    material.color.copy(block.color);
    material.color.b -= THREE.Math.randFloat(0, 0.3);
    var c = new THREE.Mesh( geometry, material );
    c.position.set(x,y,z);
    block.object3d.add(c);
    return c;
}

function isClamped(v, c) {
    return v.x == c.x || v.y == c.y || v.z == c.z;
}

function build_ca_cube(x,y,z,size,speed) {
    var ca_cube = {};
    ca_cube.size = size;
    ca_cube.grid = [];
    ca_cube.speed = speed;
    ca_cube.animate_timer = 0.0;
    ca_cube.rotate_axis = vX;
    ca_cube.rotate_direction = 1;
    ca_cube.rotate_speed = 1.0;

    ca_cube.object3d = new THREE.Object3D();
    for(var i=0; i<6; i++) {
        ca_cube.grid[i] = create_grid(size);
        ca_cube.object3d.add(ca_cube.grid[i].object3d);
    }

    //debug: looks kinda cool too I guess
    ca_cube.grid[0].set_color(blue);
    ca_cube.grid[1].set_color(new THREE.Color(0xff00ff));
    ca_cube.grid[2].set_color(green);
    ca_cube.grid[3].set_color(new THREE.Color(0xffff00));
    ca_cube.grid[4].set_color(red);
    ca_cube.grid[5].set_color(new THREE.Color(0xff9900));


    //this code sucks I will get better at flipping this stuff eventually...
    var flip = 90 * Math.PI/180;
    var nudge = (size/2) + 0.5;
    //blue top/bottom
    ca_cube.grid[0].object3d.rotation.x += flip;
    ca_cube.grid[0].object3d.position.y = nudge - 1;
    ca_cube.grid[1].object3d.rotation.x += flip;
    ca_cube.grid[1].object3d.position.y = -nudge;

    //green x sides
    ca_cube.grid[2].object3d.rotation.y -= flip;
    ca_cube.grid[2].object3d.position.x = nudge -1;
    ca_cube.grid[3].object3d.rotation.y -= flip
    ca_cube.grid[3].object3d.position.x = -nudge;

    //red z sides
    ca_cube.grid[4].object3d.position.z = nudge-1;
    ca_cube.grid[5].object3d.position.z = -nudge;

    ca_cube.object3d.position.set(x,y,z);


    //grid neighbors...
    ca_cube.grid[0].grid_opposite = ca_cube.grid[1];
    ca_cube.grid[1].grid_opposite = ca_cube.grid[0];
//    grid.grid_up
//    grid.grid_down
//    grid.grid_left
//    grid.grid_right

    fill_neighbors(ca_cube);

    populate(ca_cube, 0.3);
    //CA.game_of_life.infinite_pattern(ca_cube.grid[4]);
    /*
    for(var i=0; i<ca_cube.grid.length; i++) {
        CA.game_of_life.infinite_pattern(ca_cube.grid[i]);
    }
    */
    scene.add(ca_cube.object3d);

    ca_cube.set_speed = function(s) {
        ca_cube.speed = s;
        for(var i=0; i<ca_cube.grid.length; i++) {
            ca_cube.grid[i].speed = s;
        }
    }

    ca_cube.set_speed(ca_cube.speed);

    var cV = new THREE.Vector3(Math.PI/2*4,Math.PI/2*4,Math.PI/2*4);
    var cV_min = new THREE.Vector3(0,0,0);

    ca_cube.animate = function (delta) {
        ca_cube.animate_timer += delta;
        ca_cube.object3d.rotation.clamp(cV_min,cV);

        //is there a better way to check for clamp?
        if(ca_cube.animate_timer > 3.0 && isClamped(ca_cube.object3d.rotation, cV)) {
            console.log("wat:", ca_cube.object3d.rotation.x);
            ca_cube.object3d.rotation.copy(cV_min);
            var r = THREE.Math.randInt(0,2);
            if(r === 0) {
                ca_cube.rotate_axis = vX;
            }
            else if(r === 1) {
                ca_cube.rotate_axis = vY;
            }
            else {
                ca_cube.rotate_axis = vZ;
            }
            ca_cube.animate_timer = 0.0;
        }
        
        var R = new THREE.Vector3().copy(ca_cube.rotate_axis);
        R.multiplyScalar(ca_cube.rotate_direction*delta);
        ca_cube.object3d.rotation.add(R);
    }

    return ca_cube;
}

function populate(ca_cube, chance) {
    for(var i=0; i<ca_cube.grid.length; i++) {
        random_grid(ca_cube.grid[i], chance);
    }
}

function random_grid(grid, chance) {
    for(var i=0; i<grid.cells.length; i++) {
        var cell = grid.cells[i];
        THREE.Math.randFloat(0,1) < chance ? cell.set_state_at(0, ON) : cell.set_state_at(0,OFF);
        
        if(grid.cells[i].state[0].on === true) {
            grid.cells[i].mesh.material.visible = true;
        }
    }
}

function clear_grid(grid) {
    grid.time = 0;
    for(var i=0; i<grid.cells.length; i++) {
        grid.cells[i].clear();
    }
}

//set up neighbors
function fill_neighbors(ca_cube) {
    //the flipping may have fucked these neighbors up
    //that is: on some grid the neighbors directions could be wrong
    //so be careful when setting up grid neighbors
    for(var k=0; k<ca_cube.grid.length; k++) {
        var grid = ca_cube.grid[k];
        for(var i=0; i<grid.cells.length; i++) {
            var cell = grid.cells[i];

            //neighbors to this cell...
            var l = grid.cell_left(cell.x,cell.y);
            var r = grid.cell_right(cell.x,cell.y);
            var u = grid.cell_up(cell.x,cell.y);
            var d = grid.cell_down(cell.x,cell.y);
            var ul = grid.cell_up_left(cell.x,cell.y);
            var ur = grid.cell_up_right(cell.x,cell.y);
            var dl = grid.cell_down_left(cell.x,cell.y);
            var dr = grid.cell_down_right(cell.x,cell.y);

            //TODO: gotta deal with corner cases in the 3d ca cube...
            if(cell.x === 0) {
                //left edge cases
                if(cell.y == 0) {
                    //lower left corner
                    cell.set_ncells([r,u,ur]);
                }
                else if(cell.y == grid.size-1) {
                    //upper left corner
                    cell.set_ncells([r,d,dr]);
                }
                else {
                    //left edge
                    cell.set_ncells([u, ur, r, dr, d]);
                }
            }//end left edge cases
            else if(cell.x === grid.size-1) {
                //right edge cases
                if(cell.y === 0) {
                    //lower right corner
                    cell.set_ncells([l,ul,u]);
                }
                else if(cell.y === grid.size-1) {
                    //upper right corner
                    cell.set_ncells([l,dl,d]);
                }
                else {
                    //right edge
                    cell.set_ncells([u,ul,l,dl,d]);
                }
            }//end right edge cases
            else if(cell.x > 0) {
                if(cell.y === 0) {
                    //bottom edge
                    cell.set_ncells([l,ul,u,ur,r]);
                }
                else if(cell.y === grid.size-1) {
                    //top edge
                    cell.set_ncells([l,dl,d,dr,r]);
                }
                else {
                    //middle cases
                    cell.set_ncells([l,r,u,d,ul,ur,dl,dr]);
                }
            }
        }//end for a grid
    }//end for ca_cube
}

function grid_animate(grid, delta) {
    grid.animate_timer += delta;

    if(grid.animate_timer > grid.speed) {
        grid.animate_timer = 0.0;
        
        for(var i=0; i<grid.cells.length; i++) {
            grid.cells[i].getStateAt(grid.time).on ? grid.cells[i].mesh.material.visible = true
                : grid.cells[i].mesh.material.visible = false;
        }

        grid.time++;
    }

    if(grid.time > 20) {
        clear_grid(grid);
        random_grid(grid, 0.3);
    }

}

function create_grid(size) {
    var grid = {};
    grid.cells = [];
    grid.color = new THREE.Color(0xFF0000);
    grid.size = size;
    grid.object3d = new THREE.Object3D();
    grid.speed = 1.0;
    grid.animate_timer = 0.0;
    grid.time = 0;

    var half_size = size/2;
    for(var x=-half_size; x<half_size; x++) {
        for(var y=-half_size; y<half_size; y++) {
            var i = (y+half_size) + (size*(x+half_size));
            grid.cells[i] = new Cell(CA.game_of_life);
            grid.cells[i].x = x+half_size;
            grid.cells[i].y = y+half_size;

            grid.cells[i].mesh = new THREE.Mesh(
                new THREE.PlaneGeometry(1, 1, 1, 1),
                new THREE.MeshLambertMaterial( { color: grid.color, visible: false } )
            );
            grid.cells[i].mesh.position.set(x,y,0);
            grid.cells[i].mesh.material.side = THREE.DoubleSide;
            grid.object3d.add(grid.cells[i].mesh);
        }
    }

    grid.set_color = function(color) {
        grid.color = color;
        for(var i=0; i<grid.cells.length; i++) {
            grid.cells[i].mesh.material.color.copy(color);
        }
    };

    grid.get_cell = function(x,y) {
        return grid.cells[y+grid.size*x];
    }

    grid.set_cell = function(x,y,t,s) {
        var cell = grid.get_cell(x,y);
        s ? state = ON : state = OFF;
        cell.set_state_at(t, state);
        return cell;
    }

    //neighbor helpers...
    grid.cell_left = function(x,y) {
        if(x-1 < 0) {
            return NC;
        }
        return grid.get_cell(x-1, y);
    }

    grid.cell_right = function(x,y) {
        if(x+1 > grid.size) {
            return NC;
        }
        return grid.get_cell(x+1, y);
    }

    grid.cell_up = function(x,y) {
        if(y+1 > grid.size) {
            return NC;
        }
        return grid.get_cell(x, y+1);
    }

    grid.cell_down = function(x,y) {
        if(y-1 < 0) {
            return NC;
        }
        return grid.get_cell(x, y-1);
    }

    grid.cell_up_left = function(x,y) {
        if(x-1 < 0 || y+1 > grid.size) {
            return NC;
        }
        return grid.get_cell(x-1, y+1);
    }

    grid.cell_up_right = function(x,y) {
        if(x+1 > grid.size || y+1 > grid.size) {
            return NC;
        }
        return grid.get_cell(x+1, y+1);
    }

    grid.cell_down_left = function(x,y) {
        if(x-1 < 0 || y-1 < 0) {
            return NC;
        }
        return grid.get_cell(x-1, y-1);
    }

    grid.cell_down_right = function(x,y) {
        if(y-1 < 0 || x+1 > grid.size) {
            return NC;
        }
        return grid.get_cell(x+1, y-1);
    }

    return grid;
}

//audio
var audio_context;
window.addEventListener('load', init_audio, false);

function init_audio() {
    try {
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        audio_context = new AudioContext();
    }
    catch(e) {
        alert('Web Audio is not supported in this browser. Please try Chrome.');
    }

}

var audio_bg = null;

var xhr = new XMLHttpRequest();

xhr.addEventListener("progress", updateProgress, false);
xhr.addEventListener("load", transferComplete, false);
xhr.addEventListener("error", transferFailed, false);
xhr.addEventListener("abort", transferCanceled, false);

function load_audio() {
    //var bg =  'assets/audio/kpp_revolution.ogg';
    //var bg =  'assets/audio/oniichan_09.ogg';
    //var bg =  'assets/audio/galaxy/02.wav';
    var bg = 'assets/audio/ponponpon.wav';
    xhr.open('get', bg, true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = onload_audio;
    xhr.send();
}

function onload_audio() {
   //decode asynchrously
    if(!audio_context) {
        return;
    }
    retry = 0;
    sync = 0;
    audioData = xhr.response;
    decode();
}

//less picky decode
//http://stackoverflow.com/questions/10365335/decodeaudiodata-returning-a-null-error
function decode() {
    try{
        audio_context.decodeAudioData(audioData,
                                      play_audio,
                                      function(){ // only on error attempt to sync on frame boundary
                                          if(sync_stream()) decode();
                                      });
    } catch(e) {
        log('decode exception',e.message);
    }
}


var audioData = null;
var sync, retry;
function sync_stream() {
    var buf8 = new Uint8Array(audioData); 
    buf8.indexOf = Array.prototype.indexOf;
    var i=sync, b=buf8;
    while(1) {
        retry++;
        i=b.indexOf(0xFF,i); if(i==-1 || (b[i+1] & 0xE0 == 0xE0 )) break;
        i++;
    }
    if(i!=-1) {
        var tmp=audioData.slice(i); //carefull there it returns copy
        delete(audioData); audioData=null;
        audioData=tmp;
        sync=i;
        return true;
    }
    return false;
}

function play_audio(buffer) {
    audio_bg = buffer;
    var src = audio_context.createBufferSource();
    src.buffer = audio_bg;
    src.connect(audio_context.destination);
    src.start(0);
    start_animation = true;
}


// progress on transfers from the server to the client (downloads)
function updateProgress (evt) {
  if (evt.lengthComputable) {
    var percentComplete = evt.loaded / evt.total;
    // TODO
  } else {
      //total size is unknown
  }
}

function transferComplete(evt) {
  //alert("The transfer is complete.");
}
 
function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}
 
function transferCanceled(evt) {
  alert("The transfer has been canceled by the user.");
}

function add_stats() {
    stats = new Stats();
    stats.setMode(0); // 0: fps, 1: ms

    // Align top-left
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.left = '0px';
    stats.domElement.style.top = '0px';
    document.body.appendChild( stats.domElement );
}

function init() {
    clock = new THREE.Clock();
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    camera.position.z = 13;
    renderer = new THREE.WebGLRenderer( {antialias: true} );
    //renderer = new THREE.WebGLRenderer();

    renderer.setClearColor(0x000000,1);
    renderer.setSize( window.innerWidth, window.innerHeight );
    //controls = new THREE.TrackballControls(camera);

    //init engine
    doll.ui.init();
    doll.text.init();
    doll.fetch.init();
    doll.audio.init();
    document.body.appendChild( renderer.domElement );
}

function animate() {
    requestAnimationFrame(animate, renderer.domElement);
    render();
}

function render() {
    setInterval( function () {
        stats.begin();
    
        var delta = clock.getDelta();
        
        if(start_animation === true) {
/*
            blocks[0].disable_anim = false;
            for(var i=0; i<blocks.length; i++) {
                animate_block(blocks[i], delta);
            }
*/
            //lights.animate(delta);
            doll.ui.animate(delta);
            //animate_person(blueman,delta);
            CA_cube.animate(delta);
            for(var i=0; i<CA_cube.grid.length; i++) {
                grid_animate(CA_cube.grid[i], delta);
            }
        }
        //controls.update(delta);
        renderer.render(scene, camera);
        stats.end();
    }, 1000/60);
}

function setup_audio() {
    //var bg =  'assets/audio/kpp_revolution.ogg';
    //var bg =  'assets/audio/oniichan_09.ogg';
    //var bg =  'assets/audio/galaxy/02.wav';
    var audio = [
        'ponponpon.wav'
    ];
    doll.audio.complete = function() {
        console.log('hey I got all my audio');
        doll.audio.play(0);
        start_animation = true;
    }
    doll.audio.fetch(audio, false);
}

init();
setup_audio();
fill_scene();
add_stats();
animate();
